<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\UploadedFile;
use Exception;

/**
 * 音频转文本
 *
 * Class AudioTranscribeService
 * @package App\Services
 * @author klinson <klinson@163.com>
 * @link https://modelscope.cn/studios/frontng/paraformer
 */
class AudioTranscribeService
{
    private $baseUrl = 'https://frontng-paraformer.ms.show/gradio_api/call/transcribe_audio';

    /**
     * 上传音频文件并获取转录结果
     *
     * @param UploadedFile|string $audioFile 音频文件或本地文件路径
     * @return array
     * @throws Exception
     */
    public function transcribeAudio($audioFile)
    {
        try {
            // 第一步：上传文件并提交转录任务
            $eventId = $this->submitTranscriptionJob($audioFile);

            // 第二步：轮询获取转录结果
            $result = $this->getTranscriptionResult($eventId);

            return [
                'success' => true,
                'event_id' => $eventId,
                'result' => $result
            ];

        } catch (Exception $e) {
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * 提交转录任务
     *
     * @param UploadedFile|string $audioFile
     * @return string Event ID
     * @throws Exception
     */
    private function submitTranscriptionJob($audioFile)
    {
        // 如果是文件对象或本地文件，先上传到Gradio服务器
        if ($audioFile instanceof UploadedFile || (is_string($audioFile) && file_exists($audioFile))) {
            $uploadedPath = $this->uploadFileToGradio($audioFile);
            $fileData = [
                'path' => $uploadedPath,
                'orig_name' => $audioFile instanceof UploadedFile ? $audioFile->getClientOriginalName() : basename($audioFile),
                'meta' => [
                    '_type' => 'gradio.FileData'
                ]
            ];
        } else {
            // 如果是URL，直接使用
            $fileData = [
                'path' => $audioFile,
                'url' => $audioFile,
                'meta' => [
                    '_type' => 'gradio.FileData'
                ]
            ];
        }

        // 构建请求数据
        $requestData = [
            'data' => [$fileData]
        ];

        // 发送POST请求
        $response = Http::timeout(30)
            ->withHeaders([
                'Content-Type' => 'application/json'
            ])
            ->post($this->baseUrl, $requestData);

        if (!$response->successful()) {
            throw new Exception('Failed to submit transcription job: ' . $response->body());
        }

        $responseData = $response->json();

        // 提取event_id
        if (!isset($responseData['event_id'])) {
            throw new Exception('No event_id found in response');
        }

        return $responseData['event_id'];
    }

    /**
     * 上传文件到Gradio服务器
     *
     * @param UploadedFile|string $audioFile
     * @return string 服务器上的文件路径
     * @throws Exception
     */
    private function uploadFileToGradio($audioFile)
    {
        $uploadUrl = 'https://frontng-paraformer.ms.show/gradio_api/upload';

        if ($audioFile instanceof UploadedFile) {
            // 处理UploadedFile
            $response = Http::timeout(60)
                ->attach('files', $audioFile->getContent(), $audioFile->getClientOriginalName())
                ->post($uploadUrl);
        } else {
            // 处理本地文件路径
            if (!file_exists($audioFile)) {
                throw new Exception('Local file not found: ' . $audioFile);
            }

            $response = Http::timeout(60)
                ->attach('files', file_get_contents($audioFile), basename($audioFile))
                ->post($uploadUrl);
        }

        if (!$response->successful()) {
            throw new Exception('Failed to upload file to Gradio: ' . $response->body());
        }

        $uploadResult = $response->json();

        if (empty($uploadResult) || !isset($uploadResult[0])) {
            throw new Exception('Invalid upload response from Gradio');
        }

        return $uploadResult[0]; // 返回服务器上的文件路径
    }

    /**
     * 获取转录结果
     *
     * @param string $eventId
     * @return mixed
     * @throws Exception
     */
    private function getTranscriptionResult($eventId)
    {
        $url = $this->baseUrl . '/' . $eventId;
        $maxAttempts = 30; // 最多尝试30次
        $attempt = 0;

        while ($attempt < $maxAttempts) {
            $response = Http::timeout(10)->get($url);

            if ($response->successful()) {
                $data = $response->body();

                // 检查是否是完成状态的响应
                if (strpos($data, 'data:') === 0) {
                    $jsonData = substr($data, 5); // 移除 "data:" 前缀
                    $result = json_decode(trim($jsonData), true);

                    if (json_last_error() === JSON_ERROR_NONE && isset($result['data'])) {
                        return $result['data'];
                    }
                }

                // 如果响应包含"event: complete"，说明任务完成
                if (strpos($data, 'event: complete') !== false) {
                    // 解析最后的数据行
                    $lines = explode("\n", $data);
                    foreach (array_reverse($lines) as $line) {
                        if (strpos($line, 'data:') === 0) {
                            $jsonData = substr($line, 5);
                            $result = json_decode(trim($jsonData), true);
                            if (json_last_error() === JSON_ERROR_NONE) {
                                return $result;
                            }
                        }
                    }
                }
            }

            $attempt++;
            sleep(2); // 等待2秒后重试
        }

        throw new Exception('Transcription timeout after ' . $maxAttempts . ' attempts');
    }

    /**
     * 处理音频文件（用于URL方式，保留兼容性）
     *
     * @param UploadedFile|string $audioFile
     * @return string 文件URL或路径
     * @throws Exception
     */
    private function handleAudioFile($audioFile)
    {
        if ($audioFile instanceof UploadedFile) {
            // 如果是上传的文件，保存到临时存储并返回URL
            $filename = time() . '_' . $audioFile->getClientOriginalName();
            $path = $audioFile->storeAs('temp/audio', $filename, 'public');
            return asset('storage/' . $path);

        } elseif (is_string($audioFile)) {
            // 如果是本地文件路径
            if (file_exists($audioFile)) {
                // 对于本地文件，需要先上传到可访问的URL
                return $audioFile;
            } elseif (Storage::exists($audioFile)) {
                // 如果是Laravel storage路径
                return Storage::url($audioFile);
            } elseif (filter_var($audioFile, FILTER_VALIDATE_URL)) {
                // 如果是有效的URL
                return $audioFile;
            } else {
                throw new Exception('Audio file not found: ' . $audioFile);
            }
        }

        throw new Exception('Invalid audio file parameter');
    }
}
